﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SsTool.Controls.Workflow;
using SmartSpace.Core;
using SmartSpace.Workflow;

namespace SsTool.Controls
{
    /// <summary>
    /// Interaction logic for WorkflowDesignerPanel.xaml
    /// </summary>
    public partial class WorkflowDesignerPanel : UserControl
    {
        bool _isPicked = false;
        private BaseActivityPanel _pickedPanel = null;
        private MoveData _moveData = new MoveData();

        private List<Shape> _lines = new List<Shape>();

        private BaseActivityPanel _selectedPanel;

        public ActivityMetadata SelectedActivity { get; private set; }
        public EventMetadata SelectedEvent { get; private set; }

        public event Action<ActivityMetadata> ActivityPanelDoubleClick;
        public event Action<EventMetadata> EventPanelDoubleClick;


        private BaseActivityPanel _startPanel;
        private BaseActivityPanel _endPanel;


        public List<ActivityMetadata> AllActivities { get; private set; }

        private WorkflowMetadata _updatedWorkflow;
        private WorkflowDiagram _updatedLayout;

        public WorkflowDesignerPanel()
        {
            InitializeComponent();

            _startPanel = new StartActivityPanel();
            _startPanel.Id = new Guid(0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
            _startPanel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            _startPanel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            this._ctlCanvas.Children.Add(_startPanel);


            _endPanel = new EndActivityPanel();
            _endPanel.Id = new Guid(0x11111111, 0x1111, 0x1111, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11);
            _endPanel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            _endPanel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            this._ctlCanvas.Children.Add(_endPanel);

            this.Loaded += new RoutedEventHandler(WorkflowDesignerPanel_Loaded);
        }

        void WorkflowDesignerPanel_Loaded(object sender, RoutedEventArgs e)
        {
            if(AllActivities != null)
            {
                return;
            }
            else
            {
                AllActivities = new List<ActivityMetadata>();
            }

            if (_updatedWorkflow == null && _updatedLayout == null)
            {
                _startPanel.SetCenterPos(new Point(100, 100));
                _endPanel.SetCenterPos(new Point(100, 300));
                AddLine();
            }
            else
            {
                AddLine();
            }
        }

        public ActivityPanel2 Add(ActivityMetadata activity)
        {
            if (AllActivities == null)
            {
                AllActivities = new List<ActivityMetadata>();
            }

            AllActivities.Add(activity);

            ActivityPanel2 panel = new ActivityPanel2();
            panel.Id = activity.Id;
            panel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            panel.PreviewMouseRightButtonUp += new MouseButtonEventHandler(panel_PreviewMouseRightButtonUp);
            panel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            panel.SetCenterPos(new Point(30, 30));
            panel.DataContext = activity;

            if (_selectedPanel == null)
            {
                _startPanel.Children.Add(panel);
            }
            else
            {
                _selectedPanel.Children.Add(panel);
            }
            _ctlCanvas.Children.Add(panel);
            return panel;
        }

        public ActivityPanel2 AddNoParent(ActivityMetadata activity)
        {
            if (AllActivities == null)
            {
                AllActivities = new List<ActivityMetadata>();
            }

            AllActivities.Add(activity);

            ActivityPanel2 panel = new ActivityPanel2();
            panel.Id = activity.Id;
            panel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            panel.PreviewMouseRightButtonUp += new MouseButtonEventHandler(panel_PreviewMouseRightButtonUp);
            panel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            panel.SetCenterPos(new Point(30, 30));
            panel.DataContext = activity;

            _ctlCanvas.Children.Add(panel);
            return panel;
        }

        public void AddEvent(EventMetadata @event)
        {
            EventPanel panel = new EventPanel();
            panel.Id = @event.Id;
            panel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            panel.PreviewMouseRightButtonUp += new MouseButtonEventHandler(panel_PreviewMouseRightButtonUp);
            panel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            panel.SetCenterPos(new Point(30, 30));
            panel.DataContext = @event;

            _selectedPanel.Children.Add(panel);
            _ctlCanvas.Children.Add(panel);
        }

        public EventPanel AddEvent(EventMetadata @event, BaseActivityPanel parentPanel)
        {
            EventPanel panel = new EventPanel();
            panel.Id = @event.Id;
            panel.PreviewMouseDown += new MouseButtonEventHandler(panel_PreviewMouseDown);
            panel.PreviewMouseRightButtonUp += new MouseButtonEventHandler(panel_PreviewMouseRightButtonUp);
            panel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(panel_PreviewMouseLeftButtonDown);
            panel.SetCenterPos(new Point(30, 30));
            panel.DataContext = @event;

            parentPanel.Children.Add(panel);
            _ctlCanvas.Children.Add(panel);

            foreach (FrameworkElement item in _ctlCanvas.Children)
            {

                if (item is ActivityPanel2 && item.DataContext == @event.ToActivity
                    && @event.ToActivity != null)
                {
                    _startPanel.Children.Remove(item as BaseActivityPanel);
                    panel.Children.Add(item as BaseActivityPanel);
                }
            }
            return panel;
        }

        public void AddLine()
        {
            foreach (Shape line in _lines)
            {
                _ctlLineCanvas.Children.Remove(line);
            }
            _lines.Clear();

            BuildLine2();
        }

        public void Init(WorkflowMetadata workflow, WorkflowDiagram layout)
        {
            _updatedWorkflow = workflow;
            _updatedLayout = layout;

            List<BaseActivityPanel> elements = new List<BaseActivityPanel>();
            elements.Add(_startPanel);
            elements.Add(_endPanel);

            List<ActivityPanel2> panels = new List<ActivityPanel2>();
            foreach (ActivityMetadata activity in _updatedWorkflow.Activities)
            {
                if (activity.IsRoot)
                {
                    ActivityPanel2 panel = Add(activity);
                    panels.Add(panel);
                    elements.Add(panel);
                }
                else
                {
                    ActivityPanel2 panel = AddNoParent(activity);
                    panels.Add(panel);
                    elements.Add(panel);
                }
            }

            foreach (ActivityPanel2 item in panels)
            {
                ActivityMetadata activity = item.DataContext as ActivityMetadata;
                foreach (EventMetadata @event in activity.Events)
                {
                    elements.Add(AddEvent(@event, item));
                }
            }

            string[] panelLayouts = _updatedLayout.Data.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string panelLayout in panelLayouts)
            {
                string[] ars = panelLayout.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                Guid id = new Guid(ars[0]);
                double x = Convert.ToDouble(ars[1]);
                double y = Convert.ToDouble(ars[2]);
                foreach (BaseActivityPanel item in elements)
                {
                    if (item.Id == id)
                    {
                        item.SetCenterPos(new Point(x, y));
                    }
                }
            }

            AddLine();
        }

        private void BuildLine(BaseActivityPanel panel)
        {
            Point outPos = panel.GetOutPos();
            if (panel.Children.Count == 0)
            {
                Point inPos = this._endPanel.GetInPos();
                LinkTwoPoint(ref outPos, ref inPos, true);
            }
            else
            {
                foreach (BaseActivityPanel child in panel.Children)
                {
                    Point inPos = child.GetInPos();
                    if (child is EventPanel)
                    {
                        LinkTwoPoint(ref outPos, ref inPos, false);
                    }
                    else
                    {
                        LinkTwoPoint(ref outPos, ref inPos, true);
                    }
                    BuildLine(child);
                }
            }
        }

        private void BuildLine2()
        {
            foreach (var item in _ctlCanvas.Children)
            {
                if(item is BaseActivityPanel)
                {
                    BaseActivityPanel panel = item as BaseActivityPanel;
                    Point outPos = panel.GetOutPos();
                    if(panel.Children!=null)
                    {
                        foreach (var subItem in panel.Children)
	                    {
                            Point inPos = subItem.GetInPos();
                            if (subItem is EventPanel)
                            {
                                LinkActivityToEvent(ref outPos, ref inPos);
                            }
                            else
                            {
                                LinkEventToActivity(ref outPos, ref inPos);
                            }
                        }
                    }
                    
                }
            }
        }

        private void LinkTwoPoint(ref Point outPos, ref Point inPos, bool isArrow)
        {
            int distance = 50;
            Line lineOut = new Line();
            lineOut.X1 = outPos.X;
            lineOut.Y1 = outPos.Y;
            lineOut.X2 = outPos.X;
            lineOut.Y2 = outPos.Y + distance;
            lineOut.Stroke = Brushes.Black;
            lineOut.StrokeThickness = 1;
            _lines.Add(lineOut);
            this._ctlLineCanvas.Children.Add(lineOut);

            Line lineH = new Line();
            lineH.X1 = outPos.X;
            lineH.Y1 = outPos.Y + distance;
            lineH.X2 = inPos.X;
            lineH.Y2 = outPos.Y + distance;
            lineH.Stroke = Brushes.Black;
            lineH.StrokeThickness = 1;
            _lines.Add(lineH);
            this._ctlLineCanvas.Children.Add(lineH);

            if (isArrow)
            {
                Arrow lineIn = new Arrow();
                lineIn.X2 = inPos.X;
                lineIn.Y2 = inPos.Y;
                lineIn.X1 = inPos.X;
                lineIn.Y1 = outPos.Y + distance;
                lineIn.Stroke = Brushes.Black;
                lineIn.StrokeThickness = 1;
                lineIn.HeadWidth = 10;
                lineIn.HeadHeight = 5;

                _lines.Add(lineIn);
                this._ctlLineCanvas.Children.Add(lineIn);
            }
            else
            {
                Line lineIn = new Line();
                lineIn.X2 = inPos.X;
                lineIn.Y2 = inPos.Y;
                lineIn.X1 = inPos.X;
                lineIn.Y1 = outPos.Y + distance;
                lineIn.Stroke = Brushes.Black;
                lineIn.StrokeThickness = 1;

                _lines.Add(lineIn);
                this._ctlLineCanvas.Children.Add(lineIn);
            }
        }

        private void LinkEventToActivity(ref Point outPos, ref Point inPos)
        {
            //int distance = 50;
            //Line lineOut = new Line();
            //lineOut.X1 = outPos.X;
            //lineOut.Y1 = outPos.Y;
            //lineOut.X2 = outPos.X;
            //lineOut.Y2 = outPos.Y + distance;
            //lineOut.Stroke = Brushes.Black;
            //lineOut.StrokeThickness = 1;
            //_lines.Add(lineOut);
            //this._ctlLineCanvas.Children.Add(lineOut);

            //Line lineH = new Line();
            //lineH.X1 = outPos.X;
            //lineH.Y1 = outPos.Y + distance;
            //lineH.X2 = inPos.X;
            //lineH.Y2 = outPos.Y + distance;
            //lineH.Stroke = Brushes.Black;
            //lineH.StrokeThickness = 1;
            //_lines.Add(lineH);
            //this._ctlLineCanvas.Children.Add(lineH);

            //Arrow lineIn = new Arrow();
            //lineIn.X2 = inPos.X;
            //lineIn.Y2 = inPos.Y;
            //lineIn.X1 = inPos.X;
            //lineIn.Y1 = outPos.Y + distance;
            //lineIn.Stroke = Brushes.Black;
            //lineIn.StrokeThickness = 1;
            //lineIn.HeadWidth = 10;
            //lineIn.HeadHeight = 5;

            Arrow lineIn = new Arrow();
            lineIn.X2 = inPos.X;
            lineIn.Y2 = inPos.Y;
            lineIn.X1 = outPos.X;
            lineIn.Y1 = outPos.Y;
            lineIn.Stroke = Brushes.Black;
            lineIn.StrokeThickness = 1;
            lineIn.HeadWidth = 10;
            lineIn.HeadHeight = 5;
            _lines.Add(lineIn);
            this._ctlLineCanvas.Children.Add(lineIn);
        }

        private void LinkActivityToEvent(ref Point outPos, ref Point inPos)
        {
            Line lineIn = new Line();
            lineIn.X2 = inPos.X;
            lineIn.Y2 = inPos.Y;
            lineIn.X1 = outPos.X;
            lineIn.Y1 = outPos.Y;
            lineIn.Stroke = Brushes.Black;
            lineIn.StrokeThickness = 1;

            _lines.Add(lineIn);
            this._ctlLineCanvas.Children.Add(lineIn);
        }

        void panel_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            BaseActivityPanel panel = sender as BaseActivityPanel;
            if ((panel.DataContext is ActivityMetadata))
            {
                if (_selectedPanel != null)
                {
                    _selectedPanel.UnSelect();
                }
                _selectedPanel = panel;
                _selectedPanel.Select();
                SelectedActivity = _selectedPanel.DataContext as ActivityMetadata;
                SelectedEvent = null;
            }
            else if ((panel.DataContext is EventMetadata))
            {
                if (_selectedPanel != null)
                {
                    _selectedPanel.UnSelect();
                }
                _selectedPanel = panel;
                _selectedPanel.Select();
                SelectedActivity = null;
                SelectedEvent = _selectedPanel.DataContext as EventMetadata;
            }
        }

        void panel_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            BaseActivityPanel panel = sender as BaseActivityPanel;
            if ((panel.DataContext is ActivityMetadata))
            {
                if (_selectedPanel != null)
                {
                    _selectedPanel.UnSelect();
                }
                _selectedPanel = panel;
                _selectedPanel.Select();
                SelectedActivity = _selectedPanel.DataContext as ActivityMetadata;
                SelectedEvent = null;
                if (ActivityPanelDoubleClick != null)
                {
                    ActivityPanelDoubleClick(SelectedActivity);
                }
            }
            else if ((panel.DataContext is EventMetadata))
            {
                if (_selectedPanel != null)
                {
                    _selectedPanel.UnSelect();
                }
                _selectedPanel = panel;
                _selectedPanel.Select();
                SelectedActivity = null;
                SelectedEvent = _selectedPanel.DataContext as EventMetadata;
                if (EventPanelDoubleClick != null)
                {
                    EventPanelDoubleClick(SelectedEvent);
                }
            }
        }

        void panel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                _pickedPanel = sender as BaseActivityPanel;
                _isPicked = true;
                _moveData.BeginPos = e.GetPosition(_ctlCanvas);
                _moveData.Offset = e.GetPosition(_pickedPanel);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (_pickedPanel != null)
            {
                Point p = e.GetPosition(_ctlCanvas);
                int nx = (((int)p.X / 10) * 10);
                int ny = (((int)p.Y / 10) * 10);
                //Canvas.SetLeft(_pickedPanel,  nx - (int)_moveData.Offset.X);
                //Canvas.SetTop(_pickedPanel, ny - (int)_moveData.Offset.Y);
                _pickedPanel.SetCenterPos(new Point(nx, ny));
               AddLine();
            }
        }

        protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseUp(e);

            _isPicked = false;
            _pickedPanel = null;
        }

        public WorkflowDiagram GetWorkflowLayout(WorkflowMetadata workflow)
        {
            WorkflowDiagram layout = new WorkflowDiagram();

            layout.Id = workflow.Id;

            StringBuilder data = new StringBuilder();
            foreach (FrameworkElement item in _ctlCanvas.Children)
            {
                if (item is BaseActivityPanel)
                {
                    BaseActivityPanel panel = item as BaseActivityPanel;

                    ActivityMetadata activity = panel.DataContext as ActivityMetadata;
                    data.AppendFormat("{0}|{1}|{2},", panel.Id, panel.CenterPos.X, panel.CenterPos.Y);
                }
            }
            layout.Data = data.ToString();
            return layout;
        }
    }

    class MoveData
    {
        public Point Offset;
        public Point BeginPos;

        public MoveData()
        {
            Offset = new Point();
            BeginPos = new Point();
        }
    }
}
